[Xamarin.iOS] 実用をめざした翻訳アプリ
本エントリーは Xamarin Advent Calendar 2016 (その2) の8日目のエントリーです。
昨日のエントリーは qwerty2501 - Qiitaさんの 「Xamarin.Formsのソースコードをビルドしてテストを実行してみた」 でした。 明日の担当は tonkun - Qiitaさんです。よろしくお願いいたします。
1 はじめに
翻訳アプリを作成するために使用した技術は下記のとおりです。
- 音声入力(SFSpeechRecognizer)
- 翻訳(Microsoft Translator)
- 読み上げ(AVFoundation)
これらを使用したアプリは既に書いたことが有るのですが・・・
「入力」も「読み上げ」もクライアント側で完結するし、翻訳もそこそこ早いので、今回は、単純に 「翻訳が出来る」 というだけでなく、低機能でもいいから 「少しでも実際の会話に使えるようなもの」 というのを目標に書いてみました。 (ので、大した技術要素はありません。また、記事の内容は、過去に書いたものと多数重複しております事をお許し下さい。)
2 何とか実際の会話に使えそうなもの
この目的を達成するため、次のような 仕様を考えてみました。
- 起動すると、何もしなくても音声を受け付けるようになっている
- 翻訳ボタンなんかは、押さない(存在しない)(端末の向きを変えた時、翻訳され読み上げられる)
- 「日本語入力/英語入力」の切り替えボタンなんかは押さない(存在しない)(端末を「自分向き/相手向き」に変えると、いつでも直ちに切り替わる)
実際に、使用している様子です。
3 プロジェクト作成
プログラムの作成には、Visual Studio for Mac (Preview)を使用させて頂きました。
プロジェクトは、ファイル > 新しいソリューション で Single View App から作成しました。
今回使用した、SFSpeechRecognizer(音声入力)は、iOS 10以降の機能なので、ターゲットはiOS 10以降に設定しています。
4 追加パッケージ
パッケージの追加からSystem.Net.HttpとJson.NETを追加しました。
また、参照の編集からSystem.Xml.Ling を追加しました。
追加後のソリューションの状況です。
5 画面の回転
本アプリでは、画面の上下変化で、日本語入力と英語入力を切り替えています。
まずは、画面の回転を上下のみを許可(横向きなし)するために、info.plistのDevice orientationで、PortraitとUpside Downのみにチェックを入れます。
iPhoneの場合、デフォルトで回転の判定方法にマスクUIInterfaceOrientationMaskAllButUpsideDown がかかっているため、ViewController.csに以下の行を追加しました。
// 上下の回転を有効にする public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations() { return UIInterfaceOrientationMask.All; }
続いて、画面回転のイベントが処理できるように、UIDeviceOrientationDidChangeNotificationの通知を処理します。
NSNotificationCenter.DefaultCenter.AddObserver((Foundation.NSString)"UIDeviceOrientationDidChangeNotification", DidRotate);
public async void DidRotate(NSNotification notification) { var newLocation = UIDevice.CurrentDevice.Orientation == UIDeviceOrientation.PortraitUpsideDown; if (newLocation) { // 画面は下向き(英語入力モード) } else { // 画面は上向き(日本語入力モード) } }
6 音声入力(SFSpeechRecognizer)
音声入力には、SFSpeechRecognizerを使用しました。
SFSpeechRecognizerは、リアルタイム音声及び、録音済み音声に対応していますが、今回利用したのはリアルタイム音声による入力です。 リアルタイム音声を利用する場合の作業は、概ね次のとおりです。
- ユーザの許可(info.plist)
- マイク利用(AVAudioEngine)
- SFSpeechRecognizerの生成
- リクエストの作成
- リクエストの開始とデータ取得
(1) ユーザの許可(info.plist)
info.plistに下記の2つの設定を追加し、ユーザからの許可を得ます。
- NSMicrophoneUsageDescription(マイクの用途について)
- NSSpeechRecognitionUsageDescription(音声認識の用途について)
設定を追加するには、info.plitsをダブルクリックして開き、Sourceをタブを選択します。
Add New Entryをクリックして、新しい行を追加し、キーとその用途を設定します。
※実は、現時点(2016/12/7 Visual Studio for Mac Preview 1(7.0 build 347))で、上記の画面から日本語の入力ができなかったので、いったん、Visual Studio を終了した状態で、info.plistをVisual Studio Codeで直接編集しました。
ユーザに許可を求めるダイアログは、下記のように表示されます。どちらも許可することで、マイクと音声入力が可能になります。
(2) マイクの利用(AVAudioEngine)
マイクの利用には、AVAudioEngineを使用します。 利用の方法は、概ね次のとおりです。
// 生成 var audioEngine = new AVAudioEngine(); // 停止 audioEngine.Stop(); // 開始 audioEngine.Prepare(); audioEngine.StartAndReturnError(out err); // 入力をSFSpeechRecognizerに送る var inputNode = audioEngine.InputNode; var recordingFormat = inputNode.GetBusOutputFormat(0); inputNode.InstallTapOnBus(0, 1024, recordingFormat, (buffer, when) => { recognitionRequest?.Append(buffer); });
(3) SFSpeechRecognizerの作成
ロケールを指定してSFSpeechRecognizerを作成します。
var speechRecognizer = new SFSpeechRecognizer(new NSLocale("ja-JP"));
(4) リクエストの作成
マイク等のオーディオバッファを利用する場合は、SFSpeechAudioBufferRecognitionRequestを使用します。
var recognitionRequest = new SFSpeechAudioBufferRecognitionRequest { ShouldReportPartialResults = true };
(5) リクエストの開始とデータ取得
recognitionTaskでリクエストを開始してクロージャーで入力を取得します。 入力に変化があるたびに、開始後の全部の文字列が返されます。
recognitionTask = speechRecognizer.GetRecognitionTask(recognitionRequest, (result, error) => { if (result != null) { if (mode == Mode.Recording) { inputTextView.Text = result.BestTranscription.FormattedString; } } }
音声の入力に関しては、サンプルコードのRecognizer.csに纏まっていますので、詳しくはそちらをご参照ください。
7 翻訳
翻訳については、Microsoft Translatorを利用させて頂きました。(とりあえず無料枠がある翻訳サービスは、今、これしかないと思います) 同サービスは、1ヶ月あたり200万文字まで無料で利用可能です。
https://datamarket.azure.com/dataset/bing/microsofttranslator
端末の向きが変わった時点で、SFSpeechRecognizerの出力したテキストを同サービスで翻訳して、直ちに読み上げています。
翻訳に関しては、サンプルコードのTranslator.csに纏まっていますので、詳しくはそちらをご参照ください。
また、過去の記事ですが、参考になれば幸いです。
Xamarin.Forms 機械翻訳
8 読み上げ
読み上げには、AVFoundationを使用しました。
使い方は、非常に簡単です。
var speechSynthesizer = new AVSpeechSynthesizer(); var speechUtterance = new AVSpeechUtterance(str) { Voice = AVSpeechSynthesisVoice.FromLanguage("en"), Volume = 1.0f, }; speechSynthesizer.SpeakUtterance(speechUtterance);
本アプリでは、翻訳が完了した時点で、直ちに読み上げを行っています。
なお、読み上げの際は、音声入力で使用していた、AVAudioSessionの設定変更を忘れないようにして下さい。
var audioSession = AVAudioSession.SharedInstance(); audioSession.SetCategory(AVAudioSessionCategory.Ambient);
読み上げに関しては、サンプルコードのSpeech.csに纏まっていますので、詳しくはそちらをご参照ください。
9 最後に
英会話超弱者な私は、なんとか使える翻訳機が欲しいです。 今後、なんとか、このアプリを鍛えたいと思います。
コードは下記に置きました。気になるところが有りましたら、ぜひ教えてやってください。
[GitHub] https://github.com/furuya02/PracticalTranslator
※上記のコードを動作させるためには、翻訳サービスのキーが必要です。各自で別途ご用意下さい。
10 参考資料
メジャーアップデートしたXamarin.iOSでiOS 10のアプリ(翻訳アプリ)を作ってみた
Xamarin.Forms 機械翻訳
Xamarin記事一覧(SAPPOROWORKSの覚書)
[Developers.IO] Xamarinシリーズ
素材Library.com (イラストは、素材Library.com様のものを利用させて頂きました。)